#include "globals.h"
#include "arm7tdmi.h"
#include "utils.h"

void ARM_bx(u32 instruction)
{
	u32 reg_number = instruction & 0x0000000F;
	PC = r[reg_number];

	if((reg_number & 0x1) == 0x1)
	{
		CPSR |= (1 << 4);
	}
	else
	{
		CPSR &= ~(1 << 4);
	}
}

void ARM_b_bl(u32 instruction)
{
	u32 link =   (instruction & 0x01000000) >> 24;
	u32 offset = (instruction & 0x00FFFFFF);

	offset <<= 2;
	/* como converter para s32 ? */

	if(link)
	{
		/* como arrumar o PC ? */
	}
}

void ARM_mul_mla(u32 instruction)
{
	u32 Rd, Rn, Rs, Rm;
	u32 A, S;

	Rd = (instruction & 0x000F0000) >> 16;
	Rn = (instruction & 0x0000F000) >> 12;
	Rs = (instruction & 0x00000F00) >> 8;
	Rm = (instruction & 0x0000000F);
	A =  (instruction & 0x00200000) >> 21;
	S =  (instruction & 0x00100000) >> 20;

	/* realiza multiplicação de forma a dar certo... */

	/* setar as flags do CPSR */
}

void ARM_mull_mlal(u32 instruction)
{
	u32 RdHi, RdLo, Rn, Rm;
	u32 U, A, S;

	RdHi = (instruction & 0x000F0000) >> 16;
	RdLo = (instruction & 0x0000F000) >> 12;
	Rn   = (instruction & 0x00000F00) >> 8;
	Rm   = (instruction & 0x0000000F);
	U    = (instruction & 0x00400000) >> 22;
	A    = (instruction & 0x00200000) >> 21;
	S    = (instruction & 0x00100000) >> 20;

	/* realiza multiplicação de forma a dar certo... */

	/* setar as flags do CPSR */
}

void ARM_str_ldr(u32 instruction)
{
	/* ... */
}

u32 fetch_arm_instruction()
{
    u32 instruction;

    /* align PC if not aligned (TODO: check later if this is correct behaviour) */
    while(PC % 4 != 0)
    {
        PC++;
    }

    /* remember: PC is 2 words (8 bytes) ahead */
    instruction = ram[PC-8] | (ram[PC-7] << 8) | (ram[PC-6] << 16) | (ram[PC-5] << 24);
    PC += ARM_PC_INCREMENT;

    return instruction;
}

void decode_and_execute_arm(u32 instruction)
{
    u32 l1;

    l1 = (instruction & 0x0F000000) >> 24;

    if(l1 == 0xF)
    {
        /* software interrupt */
    }
    else if(l1 == 0xE)
    {
        if(check_bit_interval(instruction, 4, 4, 0x1))
        {
            /* Coprocessor Register Transfer */
        }
        else
        {
            /* Coprocessor Data Operation */
        }
    }
    else if((l1 >> 1) == 0x6)
    {
        /* Coprocessor Data Transfer */
    }
    else if((l1 >> 1) == 0x5)
    {
        /* Branch */
    }
    else if((l1 >> 1) == 0x4)
    {
        /* Block Data Transfer */
    }
    else if((l1 >> 2) == 0x1)
    {
        if(check_bit_interval(instruction, 4, 4, 0x1))
        {
            /* undefined instruction */
        }
        else
        {
            /* Single Data Transfer */
        }
    }
    else if((l1 >> 2) == 0)
    {
        if(check_bit_interval(instruction, 27, 4, 0x12FFF1))
        {
            /* branch and exchange */
        }
        else if(check_bit_interval(instruction, 27, 23, 0x2) &&
                check_bit_interval(instruction, 21, 20, 0x0) &&
                check_bit_interval(instruction, 11, 4, 0x9))
        {
            /* single data swap */
        }
        else if(check_bit_interval(instruction, 27, 25, 0x0) &&
                check_bit_interval(instruction, 22, 22, 0x0) &&
                check_bit_interval(instruction, 11, 7, 0x1) &&
                check_bit_interval(instruction, 4, 4, 0x1))
        {
            /* Halfword Data Transfer: register offset */
        }
        else if(check_bit_interval(instruction, 27, 25, 0x0) &&
                check_bit_interval(instruction, 22, 22, 0x1) &&
                check_bit_interval(instruction, 7, 7, 0x1) &&
                check_bit_interval(instruction, 4, 4, 0x1))
        {
            /* Halfword Data Transfer: immediate offset */
        }
        else if(check_bit_interval(instruction, 27, 22, 0x0) &&
                check_bit_interval(instruction, 7, 4, 0x9))
        {
            /* multiply */
        }
        else if(check_bit_interval(instruction, 27, 23, 0x1) &&
                check_bit_interval(instruction, 7, 4, 0x9))
        {
            /* multiply long */
        }
        else
        {
            /* data processing / psr transfer */
        }
    }
}

void fetch_and_execute_arm()
{
    /* fetch */
    u32 instruction = fetch_arm_instruction();

    /* decode and execute */
    decode_and_execute_arm(instruction);
}
